﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reflection;
using System.Text;
using EnumUtils;
using EnumUtils.Extensions;
using EnumUtilsTests.TestingEnums;
using NUnit.Framework;


namespace EnumUtilsTests
{
  [TestFixture]
  public class FlagEnumUtilitiesTests
  {
    #region General Tests
      // RJM: These tests exist to test general concepts that really aren't 
      //      specific to any particular method, but are used thoroughout the code base.
    [Test]
    [Category("General Purpose")]
    public void
    SignedToUnsignedDataLossTest( ) {
      ulong bigSignedEnumAsAnUnsignedInt = ~0UL;
      TestEnumAsIntCombination(bigSignedEnumAsAnUnsignedInt, (LargeSignedGoodFlagEnum)bigSignedEnumAsAnUnsignedInt);
      
      long bigSignedEnumAsASignedInt = ~0L;
      TestEnumAsIntCombination(bigSignedEnumAsASignedInt, (LargeSignedGoodFlagEnum)bigSignedEnumAsASignedInt);

      sbyte tinySignedEnumAsSignedInt = -1;
      TestEnumAsIntCombination(tinySignedEnumAsSignedInt, (TinySignedGoodFlagEnum)tinySignedEnumAsSignedInt);

      byte tinySignedEnumAsUnsignedInt = 0xFF;
      TestEnumAsIntCombination(tinySignedEnumAsUnsignedInt, (TinySignedGoodFlagEnum)tinySignedEnumAsUnsignedInt);
    }


    [Test]
    [Category("General Purpose")]
    public void
    UnsignedToSignedDataLossTest( ) {
      ulong bigUnignedEnumAsAnUnsignedInt = ~0UL;
      TestEnumAsIntCombination(bigUnignedEnumAsAnUnsignedInt, (LargeUnsignedGoodFlagEnum)bigUnignedEnumAsAnUnsignedInt);

      long bigUnsignedEnumAsAnSignedInt = ~0L;
      TestEnumAsIntCombination(bigUnsignedEnumAsAnSignedInt, (LargeUnsignedGoodFlagEnum)bigUnsignedEnumAsAnSignedInt);

      byte tinyUnsignedEnumAsUnUnsignedInt = 0xFF;
      TestEnumAsIntCombination(tinyUnsignedEnumAsUnUnsignedInt, (TinyUnsignedGoodFlagEnum)tinyUnsignedEnumAsUnUnsignedInt);

      sbyte tinyUnsignedEnumAsUnsignedInt = -1;
      TestEnumAsIntCombination(tinyUnsignedEnumAsUnsignedInt, (TinyUnsignedGoodFlagEnum)tinyUnsignedEnumAsUnsignedInt);

    }


    [Test]
    [Category("General Purpose")]
    public void
    BiggerIntToSmallerIntDataLossTest( ) {
      int intTooBigForATinySignedEnum = 0x100;
      bool wasOverflowExceptionThrown = false;
      try {
        TestIntToTooSmallOfAnEnumCombination(intTooBigForATinySignedEnum, (TinySignedGoodFlagEnum)intTooBigForATinySignedEnum);
      }
      catch (Exception exception) {
        TestForOverflowException(exception, out wasOverflowExceptionThrown);
      }
      Assert.IsTrue(wasOverflowExceptionThrown, "OverflowException was not thrown.");


      int intJustBigEnoughForATinySignedEnum = 0xFF;
      TestEnumAsIntCombination(intJustBigEnoughForATinySignedEnum, (TinySignedGoodFlagEnum)intJustBigEnoughForATinySignedEnum);
      
      int intTooBigForATinyUnsignedEnum = 0x100;
      try {
        TestIntToTooSmallOfAnEnumCombination(intTooBigForATinyUnsignedEnum, (TinyUnsignedGoodFlagEnum)intTooBigForATinyUnsignedEnum);
      }
      catch (Exception exception) {
        TestForOverflowException(exception, out wasOverflowExceptionThrown);
      }
      Assert.IsTrue(wasOverflowExceptionThrown, "OverflowException was not thrown.");

      int intJustBigEnoughForATinyUnsignedEnum = 0xFF;
      TestEnumAsIntCombination(intJustBigEnoughForATinyUnsignedEnum, (TinyUnsignedGoodFlagEnum)intJustBigEnoughForATinyUnsignedEnum);
    }




    [Test]
    [Category("General Purpose")]
    public void
    SmallerIntToBiggerIntDataLossTest( ) {
      sbyte bigSignedEnumAsATinySignedInt = 0x10;
      TestEnumAsIntCombination(bigSignedEnumAsATinySignedInt, (LargeSignedGoodFlagEnum)bigSignedEnumAsATinySignedInt);

      byte bigSignedEnumAsATinyUnsignedInt = 0x10;
      TestEnumAsIntCombination(bigSignedEnumAsATinyUnsignedInt, (LargeSignedGoodFlagEnum)bigSignedEnumAsATinyUnsignedInt);

      sbyte bigUnsignedEnumAsATinySignedInt = 0x10;
      TestEnumAsIntCombination(bigUnsignedEnumAsATinySignedInt, (LargeUnsignedGoodFlagEnum)bigUnsignedEnumAsATinySignedInt);

      byte bigUnsignedEnumAsATinyUnsignedInt = 0x10;
      TestEnumAsIntCombination(bigUnsignedEnumAsATinyUnsignedInt, (LargeUnsignedGoodFlagEnum)bigUnsignedEnumAsATinyUnsignedInt);

    }
    #endregion


    #region BitSheftLeft Tests
    [Test]
    [ExpectedException(typeof(ArgumentNullException))]
    public void
    BitShiftLeftNullEnumTest( ) {
      Enum nullEnum = null;
       FlagEnumUtilities.BitShiftLeft<Enum>(nullEnum, 3);
      Assert.Fail("{0} not thrown.", typeof(ArgumentNullException));
    }


    [Test]
    [ExpectedException(typeof(ArgumentException))]
    public void
    BitShiftLeftNonEnumTest( ) {
      int notAnEnum = 1;
      FlagEnumUtilities.BitShiftLeft<int>(notAnEnum, 3);
      Assert.Fail("{0} not thrown.", typeof(ArgumentException));
    }


    [Test]
    [ExpectedException(typeof(ArgumentException))]
    public void
    BitShiftLeftUsingRegularEnumTest( ) { 
      RegularEnum regularEnum = RegularEnum.f1;
      FlagEnumUtilities.BitShiftLeft<RegularEnum>(regularEnum, 3);
      Assert.Fail("Passing a regular enum should have failed, but it didn't");
    }


    [Test]
    public void
    BitShiftLeftUsingFlagsEnumTest( ) {
      Tuple<GoodFlagEnum, GoodFlagEnum, int>[] goodFlagEnumTests = {
                                                        new Tuple<GoodFlagEnum, GoodFlagEnum, int>(GoodFlagEnum.f3, GoodFlagEnum.f6, 3),
                                                        new Tuple<GoodFlagEnum, GoodFlagEnum, int>(GoodFlagEnum.f3, GoodFlagEnum.None, 4),
                                                        new Tuple<GoodFlagEnum, GoodFlagEnum, int>(GoodFlagEnum.f3, GoodFlagEnum.None, 5),
                                                      };
      // RJM: I am testing each type of Enum separately because I cannot rewrite the Enum Utilities Library to use contravance, due to various language limitations and constraints.
      foreach (Tuple<GoodFlagEnum,GoodFlagEnum, int> testDatum in goodFlagEnumTests) {
        BitShiftLeftTestOnSpecificFlagEnum(testDatum.Item1, testDatum.Item3, testDatum.Item2);
      }

      Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>[] goodFlagEnum_WithPreDefinedFlagComboTests = {
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.f2 | GoodFlagEnum_WithPreDefinedFlagCombos.f4 | GoodFlagEnum_WithPreDefinedFlagCombos.f6, 1),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.f3 | GoodFlagEnum_WithPreDefinedFlagCombos.f5 | GoodFlagEnum_WithPreDefinedFlagCombos.None, 2),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.f4 | GoodFlagEnum_WithPreDefinedFlagCombos.f6 | GoodFlagEnum_WithPreDefinedFlagCombos.None, 3),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.f5 | GoodFlagEnum_WithPreDefinedFlagCombos.None, 4),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.f6 | GoodFlagEnum_WithPreDefinedFlagCombos.None, 5),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.None, 6),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f3, GoodFlagEnum_WithPreDefinedFlagCombos.f6, 3),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f3, GoodFlagEnum_WithPreDefinedFlagCombos.None, 4),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f3, GoodFlagEnum_WithPreDefinedFlagCombos.None, 5)
                                                                                                                                              };
      foreach (Tuple<GoodFlagEnum_WithPreDefinedFlagCombos,GoodFlagEnum_WithPreDefinedFlagCombos, int> testDatum in goodFlagEnum_WithPreDefinedFlagComboTests) {
        BitShiftLeftTestOnSpecificFlagEnum(testDatum.Item1, testDatum.Item3, testDatum.Item2);
      }


      Tuple<BadFlagEnum_NotPowerOfTwoDivisible, BadFlagEnum_NotPowerOfTwoDivisible, int>[] badFlagEnum_NotPowerOfTwoDivisibleTests = {
                                                                                                                                      new Tuple<BadFlagEnum_NotPowerOfTwoDivisible, BadFlagEnum_NotPowerOfTwoDivisible, int>(BadFlagEnum_NotPowerOfTwoDivisible.f1, BadFlagEnum_NotPowerOfTwoDivisible.f4, 2),
                                                                                                                                      new Tuple<BadFlagEnum_NotPowerOfTwoDivisible, BadFlagEnum_NotPowerOfTwoDivisible, int>(BadFlagEnum_NotPowerOfTwoDivisible.f1, BadFlagEnum_NotPowerOfTwoDivisible.None, 3),
                                                                                                                                      new Tuple<BadFlagEnum_NotPowerOfTwoDivisible, BadFlagEnum_NotPowerOfTwoDivisible, int>(BadFlagEnum_NotPowerOfTwoDivisible.f1, BadFlagEnum_NotPowerOfTwoDivisible.None, 4)

                                                                                                                                     };
      foreach (Tuple<BadFlagEnum_NotPowerOfTwoDivisible,BadFlagEnum_NotPowerOfTwoDivisible, int> testDatum in badFlagEnum_NotPowerOfTwoDivisibleTests) {
        BitShiftLeftTestOnSpecificFlagEnum(testDatum.Item1, testDatum.Item3, testDatum.Item2);
      }
    }
    #endregion



    #region BitShiftRight Tests
    [Test]
    [ExpectedException(typeof(ArgumentNullException))]
    public void
    BitShiftRightNullEnumTest( ) {
      Enum nullEnum = null;
      FlagEnumUtilities.BitShiftRight<Enum>(nullEnum, 3);
      Assert.Fail("{0} not thrown.", typeof(ArgumentNullException));
    }


    [Test]
    [ExpectedException(typeof(ArgumentException))]
    public void
    BitShiftRightNonEnumTest( ) {
      int notAnEnum = 1;
      FlagEnumUtilities.BitShiftRight<int>(notAnEnum, 3);
      Assert.Fail("{0} not thrown.", typeof(ArgumentException));
    }


    [Test]
    [ExpectedException(typeof(ArgumentException))]
    public void
    BitShiftRightUsingRegularEnumTest( ) {
      RegularEnum regularEnum = RegularEnum.f3;
      FlagEnumUtilities.BitShiftRight<RegularEnum>(regularEnum, 3);
      Assert.Fail("Passing a regular enum should have failed, but it didn't");
    }


    [Test]
    public void
    BitShiftRightUsingFlagsEnumTest( ) {
      Tuple<GoodFlagEnum, GoodFlagEnum, int>[] goodFlagEnumTests = {
                                                                      new Tuple<GoodFlagEnum, GoodFlagEnum, int>(GoodFlagEnum.f5, GoodFlagEnum.f2, 3),
                                                                      new Tuple<GoodFlagEnum, GoodFlagEnum, int>(GoodFlagEnum.f5, GoodFlagEnum.f1, 4),
                                                                      new Tuple<GoodFlagEnum, GoodFlagEnum, int>(GoodFlagEnum.f5, GoodFlagEnum.None, 5)
                                                                    };

      foreach (Tuple<GoodFlagEnum,GoodFlagEnum, int> testDatum in goodFlagEnumTests) {
        BitShiftRightTestOnSpecificFlagEnum(testDatum.Item1, testDatum.Item3, testDatum.Item2);
      }

      Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>[] goodFlagEnum_WithPreDefinedFlagCombosTests = {
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.None | GoodFlagEnum_WithPreDefinedFlagCombos.f2 | GoodFlagEnum_WithPreDefinedFlagCombos.f4, 1),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.f1 | GoodFlagEnum_WithPreDefinedFlagCombos.f3, 2),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.None | GoodFlagEnum_WithPreDefinedFlagCombos.f2, 3),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.f1, 4),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5, GoodFlagEnum_WithPreDefinedFlagCombos.None, 5),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f5, GoodFlagEnum_WithPreDefinedFlagCombos.f2, 3),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f5, GoodFlagEnum_WithPreDefinedFlagCombos.f1, 4),
                                                                                                                                                new Tuple<GoodFlagEnum_WithPreDefinedFlagCombos, GoodFlagEnum_WithPreDefinedFlagCombos, int>(GoodFlagEnum_WithPreDefinedFlagCombos.f5, GoodFlagEnum_WithPreDefinedFlagCombos.None, 5)
                                                                                                                                              };
      foreach (Tuple<GoodFlagEnum_WithPreDefinedFlagCombos,GoodFlagEnum_WithPreDefinedFlagCombos, int> testDatum in goodFlagEnum_WithPreDefinedFlagCombosTests) {
        BitShiftRightTestOnSpecificFlagEnum(testDatum.Item1, testDatum.Item3, testDatum.Item2);
      }

      Tuple<BadFlagEnum_NotPowerOfTwoDivisible, BadFlagEnum_NotPowerOfTwoDivisible, int>[] badFlagEnum_NotPowerOfTwoDivisibleTests = {
                                                                                                                                       new Tuple<BadFlagEnum_NotPowerOfTwoDivisible, BadFlagEnum_NotPowerOfTwoDivisible, int>(BadFlagEnum_NotPowerOfTwoDivisible.f4, BadFlagEnum_NotPowerOfTwoDivisible.f2, 1),
                                                                                                                                       new Tuple<BadFlagEnum_NotPowerOfTwoDivisible, BadFlagEnum_NotPowerOfTwoDivisible, int>(BadFlagEnum_NotPowerOfTwoDivisible.f4, BadFlagEnum_NotPowerOfTwoDivisible.f1, 2),
                                                                                                                                       new Tuple<BadFlagEnum_NotPowerOfTwoDivisible, BadFlagEnum_NotPowerOfTwoDivisible, int>(BadFlagEnum_NotPowerOfTwoDivisible.f4, BadFlagEnum_NotPowerOfTwoDivisible.None, 3),
                                                                                                                                       new Tuple<BadFlagEnum_NotPowerOfTwoDivisible, BadFlagEnum_NotPowerOfTwoDivisible, int>(BadFlagEnum_NotPowerOfTwoDivisible.f4, BadFlagEnum_NotPowerOfTwoDivisible.None, 4)
                                                                                                                                     };
      foreach (Tuple<BadFlagEnum_NotPowerOfTwoDivisible,BadFlagEnum_NotPowerOfTwoDivisible, int> testDatum in badFlagEnum_NotPowerOfTwoDivisibleTests) {
        BitShiftRightTestOnSpecificFlagEnum(testDatum.Item1, testDatum.Item3, testDatum.Item2);
      }
    }
    #endregion



    #region ConvertEnumToBitArray Tests
    [Test]
    [ExpectedException(typeof(ArgumentNullException))]
    public void
    ConvertEnumToBitArrayNullEnumTest( ) {
      Enum nullEnum = null;
      FlagEnumUtilities.ConvertEnumToBitArray(nullEnum);
      Assert.Fail("{0} not thrown.", typeof(ArgumentNullException));
    }


    [Test]
    [ExpectedException(typeof(ArgumentException))]
    public void
    ConvertEnumToBitArrayNonEnumParameterTest( ) {
      int notAnEnum = 666;
      FlagEnumUtilities.ConvertEnumToBitArray(notAnEnum);

      Assert.Fail("{0} not thrown.", typeof(ArgumentException));
    }


    [Test]
    public void
    ConvertEnumToBitArrayOperationalCorrectnessTest( ) {
      Enum flagEnum = GoodFlagEnum.f1;
      BitArray flagEnumBits = FlagEnumUtilities.ConvertEnumToBitArray(flagEnum);
      int flagEnumAsAnInt = Convert.ToInt32(flagEnum);
      BitArray flagEnumAsAnIntBits = new BitArray(new int[] { flagEnumAsAnInt });

      IEnumerator flagEnumBitsIter = flagEnumBits.GetEnumerator();
      IEnumerator flagEnumAsAnIntBitsIter = flagEnumAsAnIntBits.GetEnumerator();

      while ((flagEnumBitsIter.MoveNext()) && (flagEnumAsAnIntBitsIter.MoveNext())) {
        Assert.AreEqual(flagEnumAsAnIntBitsIter.Current, flagEnumBitsIter.Current);
      }
    }


    [Test]
    public void
    ConvertEnumToBitArrayEnumAndBitArraySeparateInstancesTest( ) {
      Enum flagEnum = GoodFlagEnum.f1;
      BitArray flagEnumBits_IUT = FlagEnumUtilities.ConvertEnumToBitArray(flagEnum);
      BitArray flagEnumBits_originalValue = new BitArray(flagEnumBits_IUT);
      flagEnum = GoodFlagEnum.f2;

      BitArray flagEnumBits_newValue = FlagEnumUtilities.ConvertEnumToBitArray(flagEnum);

      // prove that the BitArray instance and the Enum instance can vary 
      // separately
      Assert.AreNotEqual(flagEnumBits_newValue[0], flagEnumBits_IUT[0]);
      Assert.AreNotEqual(flagEnumBits_newValue[1], flagEnumBits_IUT[1]);
      // RJM: A while loop is not used here because all the bits after the 
      //      2nd element should be the same.

      // prove that the BitArray did not, in fact, change
      IEnumerator flagEnumBits_IUTIter = flagEnumBits_IUT.GetEnumerator();
      IEnumerator flagEnumBits_originalValueIter = flagEnumBits_originalValue.GetEnumerator();
      while (flagEnumBits_IUTIter.MoveNext() && flagEnumBits_originalValueIter.MoveNext()) {
        Assert.AreEqual(flagEnumBits_originalValueIter.Current, flagEnumBits_IUTIter.Current);
      }
    } 
    #endregion



    #region DoesEnumHaveFlagsAttribute Tests
    [Test]
    [ExpectedException(typeof(ArgumentNullException))]
    public void
    DoesEnumHaveFlagsAttributeNullEnumTest( ) {
      Enum nullEnum = null;
      FlagEnumUtilities.DoesEnumHaveFlagsAttribute(nullEnum);

      Assert.Fail("{0} not thrown.", typeof(ArgumentNullException));
    }



    [Test]
    public void
    DoesEnumHaveFlagsAttributeTrueWithoutFlagsAttributeTest( ) {
      RegularEnum regularEnum = RegularEnum.f3;
      Assert.IsFalse(FlagEnumUtilities.DoesEnumHaveFlagsAttribute(regularEnum));
    }


    [Test]
    public void
    DoesEnumHaveFlagsAttributeFalseWithFlagsAttributeTest( ) {
      Enum flagsEnum = GoodFlagEnum.f3;
      Assert.IsTrue(FlagEnumUtilities.DoesEnumHaveFlagsAttribute(flagsEnum));
    } 
    #endregion


    #region GenerateBitmask Tests
    [Test]
    [ExpectedException(typeof(ArgumentNullException))]
    public void
    GenerateBitmaskEnumNullTest( ) {
      Enum nullEnum = null;
      FlagEnumUtilities.GenerateBitmask(nullEnum);

      Assert.Fail("{0} not thrown.", typeof(ArgumentNullException));
    }


    [Test]
    [ExpectedException(typeof(ArgumentException))]
    public void
    GenerateBitmaskRegularEnumTest( ) {
      RegularEnum regularEnum = RegularEnum.f1;
      FlagEnumUtilities.GenerateBitmask(regularEnum);

      Assert.Fail("{0} not thrown.", typeof(ArgumentException));
    }


    [Test]
    public void
    GenerateBitmaskOperationalCorrectnessTest( ) {
      // use bondary testing: 2^5, 2^5-1, 2^5+1
      Enum flagEnum = GoodFlagEnum.f6;
      Assert.AreEqual((64 - 1), FlagEnumUtilities.GenerateBitmask(flagEnum));
    }
    #endregion


    #region ConvertFlagEnumToIEnumerable Tests
    [Test]
    [ExpectedException(typeof(ArgumentNullException))]
    public void
    ConvertFlagEnumToIEnumerableNullEnumTest( ) {
      Enum nullEnum = null;
      FlagEnumUtilities.ConvertFlagEnumToIEnumerable(nullEnum);
      Assert.Fail("{0} not thrown.", typeof(ArgumentNullException));
    }


    [Test]
    [ExpectedException(typeof(ArgumentException))]
    public void
    ConvertFlagEnumToIEnumerableNonEnumTest( ) {
      int notAnEnum = 1;
      FlagEnumUtilities.ConvertFlagEnumToIEnumerable(notAnEnum);
      Assert.Fail("{0} not thrown.", typeof(ArgumentException));
    }


    [Test]
    [ExpectedException(typeof(ArgumentException))]
    public void
    ConvertFlagEnumToIEnumerableRegularEnumTest( ) {
      RegularEnum regularEnum = RegularEnum.f3;
      FlagEnumUtilities.ConvertFlagEnumToIEnumerable(regularEnum);
      Assert.Fail("{0} not thrown.", typeof(ArgumentException));
    }


    [Test]
    public void
    ConvertFlagEnumToIEnumerableOperationCorrectnessTest( ) {
      GoodFlagEnum flagEnum = GoodFlagEnum.All;
      IEnumerable<GoodFlagEnum> flagEnumEnumerable = FlagEnumUtilities.ConvertFlagEnumToIEnumerable(flagEnum);

      //Assert.AreEqual(true, 
      //                flagEnumEnumerable.Any(flag => flag.Equals(GoodFlagEnum.f2)));
      List<GoodFlagEnum> flagEnumList = new List<GoodFlagEnum>(flagEnumEnumerable.ToList());
      Assert.IsTrue(flagEnumList.Any(flag => flag.HasFlag(GoodFlagEnum.f2)));
    }
  #endregion



    #region ForeachEnumFlag Tests
    public event Action<bool> OperationExecuted;
    private bool m_hasOperationExecuted = false;
    private uint executionCount = 0;

    void DidOperationExecuted( bool didOperationExecuteSuccessfully ) {
      this.m_hasOperationExecuted = didOperationExecuteSuccessfully;
    }

    
    [Test]
    [ExpectedException(typeof(ArgumentNullException))]
    public void
    ForeachEnumFlagNullEnumTest( ) {
      Enum nullEnum = null;
      Action<Enum> operation = flag => Console.WriteLine(flag);
      FlagEnumUtilities.ForeachEnumFlag(nullEnum, operation);

      Assert.Fail("{0} not thrown.", typeof(ArgumentNullException));
    }


    [Test]
    [ExpectedException(typeof(ArgumentNullException))]
    public void
    ForeachEnumFlagNullOperationTest( ) {
      GoodFlagEnum flagEnum = GoodFlagEnum.f3;
      Action<GoodFlagEnum> nullOperation = default(Action<GoodFlagEnum>);
      FlagEnumUtilities.ForeachEnumFlag(flagEnum, nullOperation);

      Assert.Fail("{0} not thrown.", typeof(ArgumentNullException));
    }


    [Test]
    [ExpectedException(typeof(ArgumentException))]
    public void
    ForeachEnumFlagRegularEnumTest( ) {
      RegularEnum regularEnum = RegularEnum.f1;
      Action<RegularEnum> operation = flag => Console.WriteLine(flag);
      FlagEnumUtilities.ForeachEnumFlag(regularEnum, operation);

      Assert.Fail("{0} not thrown.", typeof(ArgumentException));
    }


    [Test]
    public void
    ForeachEnumFlagDoesOperationExecuteOnAllFlagsTest( ) {
      GoodFlagEnum flagEnum = GoodFlagEnum.All;
      Action<GoodFlagEnum> operation = flag => { Console.WriteLine(flag); ++this.executionCount; };
      FlagEnumUtilities.ForeachEnumFlag(flagEnum, operation);

      int flagEnumCount = FlagEnumUtilities.ConvertFlagEnumToIEnumerable(flagEnum).Count();
      Assert.AreEqual(flagEnumCount, this.executionCount);
    }


    [Test]
    public void
    ForeachEnumFlagDoesOperationExecuteOnEnumFlagsTest( ) {
      OperationExecuted += new Action<bool>(DidOperationExecuted);

      GoodFlagEnum_WithPreDefinedFlagCombos flagsEnum = GoodFlagEnum_WithPreDefinedFlagCombos.f1_3_5;
      Action<GoodFlagEnum_WithPreDefinedFlagCombos> operation = flag => { Console.WriteLine(flag); OperationExecuted(true); };
      FlagEnumUtilities.ForeachEnumFlag(flagsEnum, operation);

      Assert.IsTrue(this.m_hasOperationExecuted, "Foreach operation on flags enum failed.");
    }
    #endregion



    #region Enumerator-getting related tests
    [Test]
    [ExpectedException(typeof(ArgumentNullException))]
    public void
    GetEnumeratorNullEnumTest( ) {
      Enum nullEnum = null;
      FlagEnumUtilities.GetEnumerator(nullEnum);

      Assert.Fail("{0} not thrown.", typeof(ArgumentNullException));
    }
    
    
    [Test]
    public void
    GetEnumeratorOperationalCorrectnessTest( ) {
      var flagEnum = GoodFlagEnum.f1 | GoodFlagEnum.f4;
      
      IEnumerator flagEnumIter = FlagEnumUtilities.GetEnumerator(flagEnum);
      while (flagEnumIter.MoveNext()) {
        Assert.IsTrue(((GoodFlagEnum)flagEnumIter.Current & flagEnum) == (GoodFlagEnum)flagEnumIter.Current);
      }
    }


    [Test]
    [ExpectedException(typeof(ArgumentNullException))]
    public void
    GetGenericEnumeratorNullEnumTest( ) {
      Enum nullEnum = null;
      FlagEnumUtilities.GetGenericEnumerator(nullEnum);

      Assert.Fail("{0} not thrown.", typeof(ArgumentNullException));
    }


    [Test]
    public void
    GetGenericEnumeratorOperationalCorrectnessTest( ) {
      var flagEnum = GoodFlagEnum.f1 | GoodFlagEnum.f4;

      List<GoodFlagEnum> flagEnumCont = new List<GoodFlagEnum>() { GoodFlagEnum.f1, 
                                                                                             GoodFlagEnum.f4 
                                                                                           };

      IEnumerator<GoodFlagEnum> flagEnumIter = FlagEnumUtilities.GetGenericEnumerator(flagEnum);
      IEnumerator<GoodFlagEnum> flagEnumContIter = flagEnumCont.GetEnumerator();

      while (flagEnumIter.MoveNext() && flagEnumContIter.MoveNext()) {
        Assert.IsTrue((flagEnumIter.Current) == flagEnumContIter.Current);
      }
    }
    #endregion



    #region IsFlagDefined tests
    [Test]
    [ExpectedException(typeof(ArgumentNullException))]
    public void
    IsFlagDefinedNullEnumTest( ) {
      Enum nullEnum = null;
      FlagEnumUtilities.IsFlagDefined(nullEnum);

      Assert.Fail("{0} not thrown.", typeof(ArgumentNullException));
    }


    [Test]
    [ExpectedException(typeof(ArgumentException))]
    public void
    IsFlagDefinedRegularEnumTest( ) {
      RegularEnum regularEnum = RegularEnum.f4;
      FlagEnumUtilities.IsFlagDefined(regularEnum);

      Assert.Fail("{0} not thrown.", typeof(ArgumentException));
    }


    [Test]
    public void
    IsFlagDefinedValidFlagFailsTest( ) {
      Enum flagEnum = GoodFlagEnum.f6;
      Assert.IsTrue(FlagEnumUtilities.IsFlagDefined(flagEnum));
    }


    [Test]
    public void
    IsFlagDefinedInvalidFlagPassesTest( ) {
      Enum flagEnum = GoodFlagEnum.f6;
      flagEnum = (Enum)Enum.Parse(flagEnum.GetType(), (Convert.ToInt32(flagEnum) << 1).ToString());
      Assert.IsFalse(FlagEnumUtilities.IsFlagDefined(flagEnum));
    }


    [Test]
    public void
    IsFlagDefinedMultipleFlagsWithAllValidFlagFailsTest( ) {
      Enum flagEnum = GoodFlagEnum.f1 | GoodFlagEnum.f3;
      Assert.IsTrue(FlagEnumUtilities.IsFlagDefined(flagEnum));
    }


    [Test]
    public void
    IsFlagDefinedMultipleFlagsWithInvalidFlagPassesTest( ) {
      GoodFlagEnum flagEnum = GoodFlagEnum.f6;
      flagEnum = (GoodFlagEnum)Enum.Parse(flagEnum.GetType(), (Convert.ToInt32(flagEnum) << 1).ToString());
      flagEnum = flagEnum | GoodFlagEnum.f2;
      Assert.IsFalse(FlagEnumUtilities.IsFlagDefined(flagEnum));
    }


    [Test]
    public void
    IsFlagDefinedNoneFlagNotCountedWhenItShouldTest( ) {
      Enum flagEnum = GoodFlagEnum.None;
      Assert.IsTrue(FlagEnumUtilities.IsFlagDefined(flagEnum, true));
    }


    [Test]
    public void
    IsFlagDefinedNoneFlagCountedWhenItShouldntBeTest( ) {
      Enum flagEnum = GoodFlagEnum.None;
      Assert.IsFalse(FlagEnumUtilities.IsFlagDefined(flagEnum));
    }
    #endregion


    #region Helper Methods
        private void
    BitShiftLeftTestOnSpecificFlagEnum<TEnum>( TEnum flagEnum, int shiftCount, TEnum expectedEndingFlag )
      where TEnum : IConvertible {
      flagEnum = FlagEnumUtilities.BitShiftLeft(flagEnum, shiftCount);
      System.Diagnostics.Debug.WriteLine("Expected: {0}\tActual: {1}", expectedEndingFlag, flagEnum);
      Assert.AreEqual(expectedEndingFlag, flagEnum);
    }


    private void
    BitShiftRightTestOnSpecificFlagEnum<TEnum>( TEnum flagEnum, int shiftCount, TEnum expectedEndingFlag )
      where TEnum : IConvertible {
      flagEnum = FlagEnumUtilities.BitShiftRight(flagEnum, shiftCount);
      System.Diagnostics.Debug.WriteLine("Expected: {0}\tActual: {1}", expectedEndingFlag, flagEnum);
      Assert.AreEqual(expectedEndingFlag, flagEnum);
    }


    private static void
    TestEnumAsIntCombination<TEnum, TInt>( TInt enumAsAnInt, TEnum expectedResult )
      where TInt : struct, IConvertible, IFormattable
      where TEnum : IConvertible {
      TEnum parsedEnum = FlagEnumUtilities.ConvertIntToEnum<TEnum, TInt>(enumAsAnInt);
      //Console.WriteLine("The parsed integral value of {0} is {1}", typeof(TEnum).Name, parsedEnum);
      Assert.AreEqual(expectedResult, parsedEnum);
    }


    [ExpectedException(typeof(OverflowException))]
    private static void
    TestIntToTooSmallOfAnEnumCombination<TEnum, TInt>( TInt enumAsAnInt, TEnum expectedResult )
      where TInt : struct, IConvertible, IFormattable
      where TEnum : IConvertible {
        TestEnumAsIntCombination<TEnum, TInt>(enumAsAnInt, expectedResult);
      Assert.Fail("{0} not thrown", typeof(OverflowException));
    }


    private static void
    TestForOverflowException( Exception exception, out bool wasOverflowExceptionThrown ) {
      wasOverflowExceptionThrown = false;
      if (exception is OverflowException) {
        wasOverflowExceptionThrown = true;
      }
    }
    #endregion
  }
}
